﻿using gecommon;
using gerecord.Command;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace gerecord
{
    public class MemoryDB<TKey, TEntity>:IDisposable where TEntity:class
    {
        private Dictionary<TKey, MemoryEntity<TEntity>> _entities = new Dictionary<TKey, MemoryEntity<TEntity>>();

        private IReadWriter<ICommand> _queues;

        private TimeSpan _interval;

        private IWriter<ICommand> _writer;
        //目前是单线程保存只要1个就够了
        private IUpStream<TKey, TEntity> _upstream;

        private delegate Task Handle(ICommand cmd, Type type);
        private Dictionary<Type, Handle> _handles = new Dictionary<Type, Handle>();

        public void Dispose()
        {
            _upstream.Dispose();
        }

        public MemoryDB(IReadWriter<ICommand> rw, TimeSpan ts, IWriter<ICommand> rsp,IUpStreamFactory<TKey, TEntity> factory)
        {
            _queues = rw;
            _interval = ts;
            _writer = rsp;
            _upstream = factory.Create();
            _handles[typeof(RecordCommand)] = HandleRecord;
            _handles[typeof(UpdateCommand<TKey, TEntity>)] = HandleUpdate;
        }
        public void Start()
        {
            Task.Run(() => WaitAsync());
            HandleRecord(new RecordCommand(), typeof(RecordCommand)).Wait();
        }

        private async Task WaitAsync()
        {
            while (true)
            {
                var req = await _queues.ReadAsync();
                if (req == null)
                    continue;
                try
                {
                    var t = req.GetType();
                    await _handles[t](req as ICommand, t);
                }catch(Exception e)
                {
                    //打印日志
                }
            }
        }

        private async Task HandleUpdate(ICommand cmd, Type type)
        {
            var req = cmd as UpdateCommand<TKey, TEntity>;
            if(req.Entity == null)
            {//需要返回entity
                if (!_entities.ContainsKey(req.Key))
                {//内存中找不到则从上游服务器查询并返回
                    var mem = await LoadAsync(req.Key);
                    await WriteAsync(mem, req);
                }
                else
                    await WriteAsync(_entities[req.Key], req);

                return;
            }
            //进行更新
            var entity = _entities[req.Key];
            if(entity.MVersion != req.WhereMVserion || entity.MVersion >= req.MVersion)
            {//版本不匹配 发送真正的数据给他 版本要保持递增
                await WriteAsync(entity, req);
                return;
            }
            //准备更新数据
            var nv = await _upstream.UpdateAsync(entity.Entity, req.Entity);
            if (nv == entity.Entity)
                throw new Exception("UpdateAsync 不能返回旧的Entity");

            entity.Sync = false;
            entity.MVersion = req.MVersion;
            entity.Entity = nv;
            if (entity.MVersion == long.MaxValue)
            {//如果版本递增到了long.MaxValue要重置版本号 发送新的entity
                entity.MVersion = 1;
                await WriteAsync(entity, req);
            }
        }

        private async Task WriteAsync(MemoryEntity<TEntity> entity, UpdateCommand<TKey, TEntity> req)
        {
            var ec = new EntityCommand<TKey, TEntity>();
            ec.Key = req.Key;
            ec.Seq = req.Seq;
            ec.MVersion = entity.MVersion;
            ec.Entity = entity.Entity;
            await _writer.WriteAsync(ec);
        }

        private async Task HandleRecord(ICommand cmd, Type type)
        {
            var req = cmd as RecordCommand;
            //复制所有的Entity 然后准备更新
            var record = snapshot();
            Task.Run(() => { SaveWorker(record); });
        }
        //从上游服务器查询
        private async Task<MemoryEntity<TEntity>> LoadAsync(TKey key)
        {
            var entity = await _upstream.LoadAsync(key);
            var mem = new MemoryEntity<TEntity>();
            mem.Entity = entity;
            mem.MVersion = 1;//初始版本为1
            mem.Sync = true;
            _entities.Add(key, mem);
            return mem;
        }

        private async Task SaveWorker(Dictionary<TKey, TEntity> record)
        {
            try
            {
                await _upstream.SaveAsync(record);
            }
            catch(Exception e)
            {
                //打印Exception
            }
            finally
            {
                //保存完毕后
                await Task.Delay(_interval);
                await _queues.WriteAsync(new RecordCommand());//时间到了后再发送1个保存信号
            }
        }

        private Dictionary<TKey, TEntity> snapshot()
        {
            var record = new Dictionary<TKey, TEntity>();
            foreach (var item in _entities)
            {
                if(item.Value.Sync)//一个用户如果长时间不更新数据 就不需要落盘保存了
                    continue;
                record.Add(item.Key, item.Value.Entity);
                item.Value.Sync = true;
            }
            return record;
        }

        private long Now()
        {
            return DateTime.UtcNow.Ticks;
        }
    }
}
